#! /bin/bash

# This file is part of systemtap, and is free software.  You can
# redistribute it and/or modify it under the terms of the GNU General
# Public License (GPL); either version 2, or (at your option) any
# later version.


LANG=C; export LANG
PATH=/usr/sbin:/sbin:/usr/bin:/bin:$PATH; export PATH

check_error() { if test $1 != 0; then printf "\n$2\n"; exit $1; fi }

help()
{
    cat <<'EOF'
Usage: stap-prep [OPTION]... [custom_uname]
prepare system for systemtap use

Options:
[-q|--quiet]
[-h|--help]
[custom_uname]
EOF
}

UNAME=`uname -r` # determine the kernel running on the machine

# Suppress verbose output and progress indicators
bequiet=false

while [ $# -gt 0 ]; do
    case "$1" in
    -q|--quiet)
      bequiet=true
      ;;
    -h|--help)
      help
      done=true
      ;;
    *)
      UNAME=$1
      shift
      break
      ;;
    esac
    shift
done

# help given
if [ "$done" = "true" ]; then exit 0; fi

UNAME=`echo $UNAME | sed "s/ //"` #strip out any whitespace
echo "Configuring for kernel release $UNAME"


# See if the configured debuginfod
DEBUGINFOD=0
prep_debuginfod() {
    # Choose a representative part - the vmlinuz file itself, which
    # may be compressed-ELF or something else.  Previously, used a
    # vdso* image only, but that false-positive tricks stap-prep on
    # aarch64/s390x, due to rhbz 1891509 / elfutils PR25498
    vmlinuz=/lib/modules/$UNAME/vmlinuz
    
    if [ -n "$DEBUGINFOD_URLS" ]; then
        echo "Please wait, attempting to download $vmlinuz debuginfo"
        if [ -z "$DEBUGINFOD_TIMEOUT" ]; then
            DEBUGINFOD_TIMEOUT=300
            export DEBUGINFOD_TIMEOUT
            echo "Increasing DEBUGINFOD_TIMEOUT to $DEBUGINFOD_TIMEOUT temporarily"
        fi
        DEBUGINFOD_PROGRESS=1
        export DEBUGINFOD_PROGRESS
        if debuginfod-find debuginfo "$vmlinuz" > /dev/null; then
            DEBUGINFOD=1
            ls -ald `debuginfod-find debuginfo "$vmlinuz"`
            echo "Download successful.  Assuming debuginfod server usage."
        else
            echo "Download failed."
        fi
    else
        echo 'Suggestion: consider configuring automatic debuginfo downloading via debuginfod.'
    fi
}


prep_rpm_based() {
# uname -r can produce different kinds of output:
# 2.6.32-30.el6.x86_64 (no variant, but including ".arch")
# 2.6.18-194.3.1.el5debug ("variant", without dot, no arch)
# 2.6.33.4-95.fc13.i686.PAE (".arch.variant", two dots)
# 3.18.6-100.fc20.i686+PAE (".arch+variant", dot, plus)
KERNEL="kernel"
for VARIANT in debug kdump PAE xen; do
  # strip out ".variant" or else "+variant" or else "variant" at end.
  TMP=`echo $UNAME | sed "s/[\.\+]\?$VARIANT\$//"`
  if [ "$TMP" != "$UNAME" ]; then
      UNAME=$TMP; KERNEL="kernel-$VARIANT"
  fi
done
# and then we have rhel9 era kernel-rt's:
# 5.14.0-200.rt14.201.el9 ->
#      "kernel-rt-debug-5.14.0-200.rt14.201.el9"
# OR?! "kernel-rt-5.14.0-200.rt14.201.el9"
if expr "$UNAME" : ".*\.rt.*" >/dev/null;
then
    KERNEL=`echo $KERNEL | sed -e s,kernel,kernel-rt,`
fi

KERN_ARCH=`uname -m`
KERN_REV=`echo $UNAME | sed s/.$KERN_ARCH//` # strip arch from uname
if [ -x /usr/bin/dnf4 ]; then
    DI="dnf4 debuginfo-install"
    DI_DEPS=""
    D="dnf4"
elif [ -x /usr/bin/dnf-3 ]; then
    DI="dnf-3 debuginfo-install"
    DI_DEPS=""
    D="dnf-3"
elif [ -x /usr/bin/dnf ]; then
    DI="dnf debuginfo-install"
    DI_DEPS=""
    D="dnf"
else
    DI="debuginfo-install"
    DI_DEPS="yum-utils"
    D="yum"
fi
if test "$bequiet" = "true"; then
    DI="$DI --quiet"
    D="$D --quiet"
fi
CANDIDATES="$KERNEL-$KERN_REV.$KERN_ARCH \
  $KERNEL-devel-$KERN_REV.$KERN_ARCH \
  $DI_DEPS"
if [ "$DEBUGINFOD" -eq 0 ]; then # add debuginfo search if not already 
    CANDIDATES="$CANDIDATES $KERNEL-debuginfo-$KERN_REV.$KERN_ARCH"
fi
NEEDED=`rpm --qf "%{name}-%{version}-%{release}.%{arch}\n" \
    -q $CANDIDATES | grep "is not installed" | awk '{print $2}'`
if [ "$NEEDED" != "" ]; then
    echo -e "Need to install the following packages:\n$NEEDED"
    if [ `id -u` = "0" ]; then #attempt to install
        $D install -y --enablerepo=\* $NEEDED
        if expr "$NEEDED" : ".*debuginfo.*" >/dev/null;
        then
            $DI -y $KERNEL-$KERN_REV.$KERN_ARCH;
        fi
        rpm -q $NEEDED
        rc=$?
        check_error $rc "problem installing rpm(s) $NEEDED\nin case of file conflicts, try again after # $D erase $KERNEL-debuginfo"
    fi
fi
}



prep_deb_based() {
if [ $# -ne 0 ]; then
    echo "Specifying kernel version is not yet support on deb based systems." 1>&2
    exit 1
fi

# 2.6.32-5-amd64
# 2.6.32-37-generic
ABINAME="$(cut -d " " -f 3 /proc/version)"

# 2.6.32
BASEVERSION="$(echo "$ABINAME" | cut -d "-" -f 1)"
DEBIAN_FRONTEND=noninteractive # don't confirm or chat
export DEBIAN_FRONTEND

case "$DISTRO" in 
    Debian) # 2.6.32-39
	if uname -v | grep -q Debian; then
	    VERSION="$(uname -v | cut -d " " -f 4)"
	else
            VERSION="$(cut -d " " -f 3 /proc/version)"
	fi
	;;
    Ubuntu)
	# 2.6.32-37.81
        if [ -f /proc/version_signature ]; then
  	    VERSION="$(cut -d " " -f 2 /proc/version_signature | cut -d "-" -f 1-2)"
        else # 4.18
            VERSION="$(cut -d " " -f 3 /proc/version)"
        fi
	;;
esac

(
    echo "make >= 0"
    echo "linux-image-$ABINAME = $VERSION"
    echo "linux-headers-$ABINAME = $VERSION"
    if [ "$DEBUGINFOD" -eq 0 ]; then # add dbgsym search if not already 
        echo "linux-image-$ABINAME-dbgsym = $VERSION"
    fi
) | while read package relation requiredversion; do
    installedversion="$(dpkg-query -W "$package" 2> /dev/null | cut -f 2)"
    if [ "$installedversion" = "" ]; then
	availableversion="$(apt-cache show $package 2> /dev/null | grep ^Version: | cut -d " " -f 2)"
	if [ "$availableversion" = "" -a "$(echo $package | grep dbgsym$)" ]; then
	    echo "You need package $package but it does not seem to be available"
	    if [ "$DISTRO" = "Ubuntu"  ]; then
		echo " Ubuntu -dbgsym packages are typically in a separate repository"
		echo " Follow https://wiki.ubuntu.com/DebuggingProgramCrash to add this repository"
	    elif [ "$DISTRO" = "Debian" ]; then
		echo " Debian -dbgsym packages are typically in a separate repository"
		echo " Follow https://wiki.debian.org/AutomaticDebugPackages to add this repository"
	    fi
	else
	    echo "Need to install $package"
            if [ `id -u` = "0" ]; then #attempt to install
                q=""; test "$bequiet" = "true" && q='--quiet'
                apt-get $q -y install $package
            fi
	fi
    elif ! dpkg --compare-versions $installedversion $relation $requiredversion; then
	echo "Package $package version $installedversion does not match version of currently running kernel: $requiredversion"
	echo " Consider apt-get upgrade && reboot"
    fi
done

user="$(id --user --name)"
if [ "$user" != "root" ]; then
    groups="$(id --groups --name)"
    for i in stapusr stapdev; do
	if [ "$(echo $groups | grep $i)" = "" ]; then
	    echo "Be root or adduser $user $i"
	fi
    done
fi
}


prep_debuginfod "$@"
DISTRO="$(lsb_release --id --short 2> /dev/null)"
case "$DISTRO" in
Debian|Ubuntu)
	prep_deb_based "$@"
	;;
*)
	prep_rpm_based "$@"
	;;
esac
